import { isInterval } from '@/ayaka'
import { DpHarvestTask } from './types'
import { CreepRoles } from '@/modules/structures/spawn/types'
import SpawnController from '@/modules/structures/spawn'
import { BodyPartArrayType, getCreepWorking } from '@/utils/common'
import { getFreeSpace, unserializePos } from '@/utils/position'
import { goTo } from '@/modules/move'

/**
 * 孵化 dpCarrier
 */
export const spawnDpCarrier = function (room: MyRoom, task: DpHarvestTask, dpHarvesters: Creep[], dpCarriers: Creep[]) {
    if (!isInterval(31)) return

    if (SpawnController.searchSpawnTaskByRole(room, CreepRoles.dpCarrier).length) return

    const { distance, lastCooldown } = task

    // 存活时间较长的爬
    const longTimeHarvesters = dpHarvesters.filter((creep) => {
        return creep.spawning || (creep.ticksToLive && creep.ticksToLive > (distance || 150) + creep.body.length * 3)
    })

    const longTimeCarriers = dpCarriers.filter((creep) => {
        return creep.spawning || (creep.ticksToLive && creep.ticksToLive > (distance || 150) + creep.body.length * 3)
    })

    if (longTimeHarvesters.length <= 0 || longTimeCarriers.length >= 2) return

    // 计算出多少个
    let work = 0
    longTimeHarvesters.forEach((creep) => {
        if (!creep.memory.data) {
            creep.memory.data = {}
        }

        if (!creep.memory.data.work) {
            creep.memory.data.work = creep.getActiveBodyparts(WORK)
        }

        work += creep.memory.data.work
    })

    const carry = Math.ceil((work * (distance || 300) * 2) / Math.max(lastCooldown, 5) / 30)
    const hasCarry = longTimeCarriers.reduce((sum, creep) => {
        if (!creep.memory.data) {
            creep.memory.data = {}
        }

        if (!creep.memory.data.carry) {
            creep.memory.data.carry = creep.getActiveBodyparts(CARRY)
        }

        return sum + creep.memory.data.carry
    }, 0)
    if (hasCarry >= carry) return

    const creepName = SpawnController.addSpawnTask(room, {
        role: CreepRoles.dpCarrier,
        body: getDpCarrierBody(room, task, carry),
        defaultAdaption: true,
    })
    if (creepName) {
        task.carriers.push(creepName)
    }
}

/**
 * 生成 dpCarrier 身体部件
 */
const getDpCarrierBody = function (room: Room, task: DpHarvestTask, carry: number): BodyPartArrayType {
    if (task.lastCooldown > 40) carry = 12
    if (carry > 25) carry = 25
    if (carry < 12) carry = 10
    return [[[CARRY, MOVE], carry]]
}

/**
 * dpCarrier 工作
 */
export const dpCarrierWork = function (room: MyRoom, task: DpHarvestTask, harvesters: Creep[], carriers: Creep[]) {
    const { posStr, depositType, distance } = task
    const depositPos = unserializePos(posStr)
    if (!depositPos) {
        console.log(`[dpHarvesterWork] invalid posStr: ${posStr}`)
        task.done = true
        return
    }

    carriers.forEach((creep) => {
        if (creep.memory.boost && !creep.memory.boost.done) {
            return
        }

        if (creep.hits < creep.hitsMax) {
            if (!task.restartTime || task.restartTime < Game.time) {
                task.restartTime = Game.time + 1500
            }
        }

        getCreepWorking(creep)

        if (creep.memory.working) {
            let target: StructureStorage | StructureTerminal | null = null

            if (!creep.memory.targetId) {
                const myRoom = Game.rooms[creep.memory.belong]
                if (myRoom.storage && myRoom.storage.store.getFreeCapacity() > 10000) {
                    target = myRoom.storage
                } else if (myRoom.terminal && myRoom.terminal.store.getFreeCapacity() > 10000) {
                    target = myRoom.terminal
                }

                if (!target) {
                    return
                }

                creep.memory.targetId = target.id
            }

            if (
                !target &&
                !(target = Game.getObjectById(creep.memory.targetId as Id<StructureStorage | StructureTerminal>))
            ) {
                delete creep.memory.targetId
                return
            }

            if (creep.pos.isNearTo(target)) {
                const result = creep.transfer(target, depositType)
                if (result === OK) {
                    // 剩余寿命不足以再搬一趟就自杀
                    if (creep.ticksToLive! < (distance || 300) * 2 + 200) {
                        creep.suicide()
                    }
                } else {
                    delete creep.memory.targetId
                }
                return
            }

            goTo(creep, target.pos, { preferHighway: true })
        } else {
            if (creep.memory.slack) {
                return
            }

            // 出发
            if (creep.room.name !== depositPos.roomName) {
                goTo(creep, depositPos, { preferHighway: true })
                return
            }

            if (creep.ticksToLive! < (distance || 300) + 100) {
                if (creep.store[depositType] > 0) {
                    creep.memory.working = true
                } else {
                    creep.suicide()
                }
                return
            }

            const dpHarvesters = harvesters.filter((c) => c.store[depositType] > 0)

            if (dpHarvesters.length) {
                const targetHarvester = dpHarvesters[0]
                if (creep.pos.isNearTo(targetHarvester)) {
                    targetHarvester.transfer(creep, depositType)
                    return
                }

                creep.moveTo(targetHarvester)
                return
            }

            // 堵路了就移动到旁边
            if (creep.pos.isNearTo(depositPos)) {
                const freePos = getFreeSpace(depositPos, { ignoreCreep: false })
                const targetPos = freePos.find((pos) => !pos.isNearTo(depositPos))
                if (targetPos) {
                    creep.move(creep.pos.getDirectionTo(targetPos))
                }
                return
            }

            if (creep.pos.getRangeTo(depositPos) > 3) {
                goTo(creep, depositPos, { range: 3 })
                return
            }

            creep.memory.slack = Math.min(
                ...harvesters.map((c) => Math.max((c.ticksToLive || 1500) - 10, 0)),
                Math.max(creep.ticksToLive! - (distance || 300) - 120, 0),
                15,
            )
        }
    })
}
